use libunftp::auth::{self, UserDetail};
use core::panic;
use std::fmt::{Display, Formatter};
use std::collections::HashMap;
use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;
use std::fs;
use std::env;

use unftp_sbe_fs::ServerExt;

use std::future::Future;

use env_logger::Builder;
use libunftp::auth::Authenticator;
use libunftp::auth::DefaultUser;
use log::{info, debug,error, LevelFilter};

use unftp_sbe_fs::Filesystem;

/**
 * 读取配置文件
 * 命令： moon-ftp-server -Config path
 * 配置文件有以下几个配置：
 * port= 端口号:  默认2121
 * passtive_port_from=端口号: 被动模式随机端口号最小值， 默认50000
 * passtive_port_end=端口号:  被动模式随机端口号最小值， 默认65535
 * username=username: 默认值username
 * password=password: 默认值password
 * home_dir=/home/bob/ftpdir/, 必须指定
 * log_level=info/debug/warn/error, 默认info
 *
 */
#[tokio::main]
pub async fn main() {
    const CONFIG_PORT: &str = "port";
    const CONFIG_PASSIVE_PORT_FROM: &str="passive_port_from";
    const CONFIG_PASSIVE_PORT_END: &str="passive_port_end";
    const CONFIG_USERNAME: &str = "username";
    const CONFIG_PASSWORD: &str = "password";
    const CONFIG_HOME_DIR:&str="home_dir";
    const CONFIG_LOG_LEVEL:&str = "log_level";

    const DEFAULT_PORT: &str="2121";
    const DEFAULT_PASSIVE_PORT_FROM:u16=50000;
    const DEFAULT_PASSIVE_PORT_END:u16=65535;
    const DEFAULT_USERNAME: &str="username";
    const DEFAULT_PASSWORD: &str="password";


    let args: Vec<String> = env::args().collect();
	if args.len() != 3 || args[1]!="-config"{
		panic!("请指定配置文件路径，例如 moon-ftp-server -config /home/config/moon-ftp-server.properties");
	}

    //
    let config_file_path = &args[2];
    let config_content = fs::read_to_string(config_file_path).expect("无法读取配置文件，请检查配置文件是否有误！");
    let config_map :HashMap<String, String>= config_content.lines().filter(|line| !line.trim().is_empty()).map(|line| {
        let mut part = line.split('=');
        let key = part.next().unwrap().trim().to_string();
        let value = part.next().unwrap().trim().to_string();
        (key, value)
    }).collect();

    //设置日志级别, 如果hashmap 没有key, 会怎样？返回None
    let log_level: LevelFilter = config_map.get(CONFIG_LOG_LEVEL)
        .map(|level| match level.to_lowercase().as_str() {
            "info" => LevelFilter::Info,
            "debug" => LevelFilter::Debug,
            "warn" => LevelFilter::Warn,
            "error" => LevelFilter::Error,
            "trace" => LevelFilter::Trace,
            "off" => LevelFilter::Off,
            _ => LevelFilter::Info,
        })
        .unwrap_or(LevelFilter::Info);

    Builder::new()
        .filter_level(log_level) // 设置日志级别
        .init();

    //ftp 端口号
    let port: u16 = config_map.get(CONFIG_PORT)
        .map(|port_str| port_str.parse().expect("无效的端口号格式！"))
        .unwrap_or_else(|| DEFAULT_PORT.parse().expect("默认端口号设置失败！"));

    //passtive port
    let port_from :u16= config_map.get(CONFIG_PASSIVE_PORT_FROM)
        .map(|port_str| port_str.parse().expect("无效的端口号格式！"))
        .unwrap_or_else(|| DEFAULT_PASSIVE_PORT_FROM);

    let port_end :u16= config_map.get(CONFIG_PASSIVE_PORT_END)
        .map(|port_str| port_str.parse().expect("无效的端口号格式！"))
        .unwrap_or_else(|| DEFAULT_PASSIVE_PORT_END);


    //user info
    let username = config_map.get(CONFIG_USERNAME)
        .map(|s| s.to_string())
        .unwrap_or_else(|| DEFAULT_USERNAME.to_string());

    let password = config_map.get(CONFIG_PASSWORD)
        .map(|s| s.to_string())
        .unwrap_or_else(|| DEFAULT_PASSWORD.to_string());

    let home = config_map.get(CONFIG_HOME_DIR)
        .map(|s| s.to_string())
        .unwrap_or_else(|| panic!("必须设置FTP根目录！"));

    //检查目录存在
    if !Path::new(&home).exists() {
        panic!("FTP根目录 {} 不存在！", home);
    }

    // 检查目录可读性
    /*
    if !Path::new(&home).metadata().unwrap().permissions().readonly() {
        panic!("FTP根目录 {} 不可读！", home);
    }
    */
    //ftp 规范， /结尾
    let current_login_user = CustomUser::new(username, password, home);

    info!("Starting FTP server on port {} with home directory {}", port, &current_login_user.home);

/*
    let server  = libunftp::Server::with_fs(&current_login_user.home)
        .authenticator(Arc::new(CustomAuthenticator::new(current_login_user)))
        .greeting("Welcome to the Moon ftp server^_^ ......")
        .passive_ports(port_from..port_end)
        .build()
        .unwrap();

    */
    let home_dir = current_login_user.home_string();
    debug!("------------------> home dir {}",&home_dir);
    let server = libunftp::Server::with_fs(home_dir)
        .ftps_required(libunftp::options::FtpsRequired::All, libunftp::options::FtpsRequired::None)
        .ftps("/home/owen/code/CodeTest/rust/moon-ftp-server/server.crt",
            "/home/owen/code/CodeTest/rust/moon-ftp-server/server.key")
        .authenticator(Arc::new(CustomAuthenticator::new(current_login_user)))
        .idle_session_timeout(600) //timeout 600 seconds
        .greeting("Welcome to the Moon ftp server^_^ ......")
        .passive_ports(port_from..port_end)
        .build()
        .unwrap();

    let _ = server.listen(format!("0.0.0.0:{}",port)).await
        .expect("Moon FTP server start failed!"); //如果写127.0.0.1, 那么只有相同的机器才能访问，0.0.0.0 所有的ip 都可以访问
}

#[derive(Debug, Default, Clone)]
struct CustomAuthenticator {
    //应该有一个CustomUser属性，从配置文件中读取用户名密码去匹配客户端输入的信息
    user:CustomUser,
}

impl  CustomAuthenticator{
    pub fn new(user: CustomUser) ->Self {
        CustomAuthenticator{
            user
        }
    }
}


impl Authenticator<DefaultUser> for CustomAuthenticator {
    /**
     * 代码解读：
     * 这里的返回值挺复杂的 Pin<Box<dyn Futrue<Result>>>
     * 最终返回一个Futrue
     * 如何返回future: https://course.rs/advance/async/async-await.html
     * ```
     * async { ... } 这样可以生成一个futrue 特征值
     * ```
     *
     * Futrue
     */
    fn authenticate<'life0, 'life1, 'life2, 'async_trait>(
        &'life0 self,
        username: &'life1 str,
        creds: &'life2 auth::Credentials,
    ) -> Pin<
        Box<
            dyn Future<Output = Result<DefaultUser, auth::AuthenticationError>>
                + Send
                + 'async_trait,
        >,
    >
    where
        'life0: 'async_trait,
        'life1: 'async_trait,
        'life2: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            info!("username from client {}, pass from client {}", username, &creds.password.as_ref().unwrap());
            info!("username from config {}, pass from config {}", &self.user.name, &self.user.password);
            // 1. 验证用户名密码
            if username != self.user.name || creds.password.as_deref() != Some(&self.user.password) {
                error!("Authentication failed: invalid credentials");
                return Err(auth::AuthenticationError::BadUser);
            }

            // 2. 验证目录可访问性
            let home_path = Path::new(&self.user.home);
            if !home_path.exists() {
                error!("Home directory {} does not exist", self.user.home);
                return Err(auth::AuthenticationError::BadCert);
            }
            info!(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Authentication.........");

            // 3. 尝试读取目录（验证权限）
            if let Err(e) = fs::read_dir(home_path) {
                error!("Cannot access home directory: {:?}", e);
                return Err(auth::AuthenticationError::BadCert);
            }
            info!(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Authentication succeeded for {}", username);

            Ok(DefaultUser{})
        })
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Default)]
struct CustomUser {
    name: String,
    password: String,
    home:String,
}

impl CustomUser {
    fn new(name: String, password: String, home: String ) -> Self {
        CustomUser { name, password, home }
    }

    fn home_string(&self) ->String{
        self.home.clone()
    }
}

impl Display for CustomUser {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "DefaultUser")
    }
}

impl UserDetail for CustomUser {

    fn home(&self) -> Option<&Path> {
        let path = Path::new(&self.home);
        // 检查路径是否存在（二次确认）
        if !path.exists() {
            error!("Home directory {} does not exist!", self.home);
            return None;
        }
        Some(path)
    }
}
